﻿using QInjection.Injector.Contracts;
using Roslyn.Compilers.CSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace QInjection.Injector
{
    internal class RuleValidator
    {
        internal static void Validate(Type ntype, BinaryExpressionSyntax node, Restriction ruleType, List<RuleData> list)
        {
            switch (ruleType)
            {
                case Restriction.Operator:
                    {
                        var operators = (from x in list select (Operator)x.Value).ToList();
                        RuleValidator.CheckOperator(ntype, node.OperatorToken, operators);
                    }
                    break;
                default:
                    throw new Exception(String.Format("Rule {0} Not Supported on {1}", ruleType, ntype.FullName));
            }
        }

        internal static void Validate(Type ntype, IdentifierNameSyntax node, Restriction ruleType, List<RuleData> list)
        {
            switch (ruleType)
            {
                default:
                    throw new Exception(String.Format("Rule {0} not supported on {1}", ruleType, ntype.FullName));
            }
        }

        internal static void Validate(Type ntype, LiteralExpressionSyntax node, Restriction ruleType, List<RuleData> list)
        {
            switch (ruleType)
            {
                default:
                    throw new Exception(String.Format("Rule {0} not supported on {1}", ruleType, ntype.FullName));
            }
        }

        internal static void Validate(Type ntype, ParameterSyntax node, Restriction ruleType, List<RuleData> list)
        {
            switch (ruleType)
            {
                default:
                    throw new Exception(String.Format("Rule {0} not supported on {1}", ruleType, ntype.FullName));
            }
        }

        internal static void Validate(Type ntype, MemberAccessExpressionSyntax node, Restriction ruleType, List<RuleData> list)
        {
            switch (ruleType)
            {
                case Restriction.Name:
                    {
                        if (node.Name is IdentifierNameSyntax)
                        {
                            var names = (from x in list select (String)x.Value).ToList();
                            String name = (node.Name as IdentifierNameSyntax).Identifier.ValueText;
                            RuleValidator.CheckName(ntype, name, names);
                        }
                        else
                            throw new Exception("Only IdentifierNameSyntax are supported as Name of MemberAccessExpressionSyntax");
                    }
                    break;
                case Restriction.NameRegex:
                    {
                        if (node.Name is IdentifierNameSyntax)
                        {
                            var namesregex = (from x in list select (Regex)x.Value).ToList();
                            String name = (node.Name as IdentifierNameSyntax).Identifier.ValueText;
                            RuleValidator.CheckNameRegex(ntype, name, namesregex);
                        }
                        else
                            throw new Exception("Only IdentifierNameSyntax are supported as Name of MemberAccessExpressionSyntax");
                    }
                    break;
                default:
                    throw new Exception(String.Format("Rule {0} not supported on {1}", ruleType, ntype.FullName));
            }
        }

        internal static void Validate(Type ntype, InvocationExpressionSyntax node, Restriction ruleType, List<RuleData> list)
        {
            switch (ruleType)
            {
                case Restriction.Arity:
                    {
                        Int32 argcount = 0;
                        if (node.ArgumentList == null)
                            argcount = 0;
                        else
                            argcount = node.ArgumentList.Arguments.Count();
                        if (argcount != (Int32)list[0].Value)
                            throw new Exception(String.Format("Arity error on node {0} (read {1} but expected {2})", ntype, argcount, (Int32)list[0].Value));
                    }
                    break;
                case Restriction.Name:
                    {
                        if (node.Expression is MemberAccessExpressionSyntax)
                        {
                            var memberexpr = node.Expression as MemberAccessExpressionSyntax;
                            if (memberexpr.Name is IdentifierNameSyntax)
                            {
                                var names = (from x in list select (String)x.Value).ToList();
                                String name = (memberexpr.Name as IdentifierNameSyntax).Identifier.ValueText;
                                RuleValidator.CheckName(ntype, name, names);
                            }
                            else
                                throw new Exception("Only IdentifierNameSyntax are supported as Name of MemberAccessExpressionSyntax");
                        }
                        else
                            throw new Exception("Only MemberAccessExpressionSyntax are supported as entity of InvocationExpressionSyntax");
                    }
                    break;
                case Restriction.NameRegex:
                    {
                        if (node.Expression is MemberAccessExpressionSyntax)
                        {
                            var memberexpr = node.Expression as MemberAccessExpressionSyntax;
                            if (memberexpr.Name is IdentifierNameSyntax)
                            {
                                var namesregex = (from x in list select (Regex)x.Value).ToList();
                                String name = (memberexpr.Name as IdentifierNameSyntax).Identifier.ValueText;
                                RuleValidator.CheckNameRegex(ntype, name, namesregex);
                            }
                            else
                                throw new Exception("Only IdentifierNameSyntax are supported as Name of MemberAccessExpressionSyntax");
                        }
                        else
                            throw new Exception("Only MemberAccessExpressionSyntax are supported as entity of InvocationExpressionSyntax");
                    }
                    break;
                default:
                    throw new Exception(String.Format("Rule {0} not supported on {1}", ruleType, ntype.FullName));
            }
        }


        internal static void Validate(Type ntype, ArgumentSyntax node, Restriction ruleType, List<RuleData> list)
        {
            switch (ruleType)
            {
                case Restriction.Reference:
                    {
                        var reforout = node.RefOrOutKeyword;
                        if (reforout != null && reforout.Kind != SyntaxKind.None)
                        {
                            Reference reference = reforout.Kind == SyntaxKind.RefKeyword ? Reference.Reference : Reference.Outupt;
                            var count = list.Where(x => ((Reference)x.Value) == reference).Count();
                            if (count == 0)
                                throw new Exception(String.Format("Token {0} not supported for node {1}", reference, ntype));
                            if (count > 1)
                                throw new Exception(String.Format("Malformed name-rule for node {0} with name {1}", ntype, reference));
                        }
                    }
                    break;
                default:
                    throw new Exception(String.Format("Rule {0} not supported on {1}", ruleType, ntype.FullName));
            }
        }

        internal static void Validate(Type ntype, PrefixUnaryExpressionSyntax node, Restriction ruleType, List<RuleData> list)
        {
            switch (ruleType)
            {
                case Restriction.Operator:
                    {
                        var operators = (from x in list select (Operator)x.Value).ToList();
                        RuleValidator.CheckOperator(ntype, node.OperatorToken, operators);
                    }
                    break;
                default:
                    throw new Exception(String.Format("Rule {0} Not Supported on {1}", ruleType, ntype.FullName));
            }
        }

        internal static void Validate(Type ntype, PostfixUnaryExpressionSyntax node, Restriction ruleType, List<RuleData> list)
        {
            switch (ruleType)
            {
                case Restriction.Operator:
                    {
                        var operators = (from x in list select (Operator)x.Value).ToList();
                        RuleValidator.CheckOperator(ntype, node.OperatorToken, operators);
                    }
                    break;
                default:
                    throw new Exception(String.Format("Rule {0} Not Supported on {1}", ruleType, ntype.FullName));
            }
        }

        internal static void Validate(Type ntype, ConditionalExpressionSyntax node, Restriction ruleType, List<RuleData> list)
        {
            switch (ruleType)
            {
                default:
                    throw new Exception(String.Format("Rule {0} not supported on {1}", ruleType, ntype.FullName));
            }
        }

        internal static void Validate(Type ntype, SyntaxNode node, Restriction ruleType, List<RuleData> list)
        {
            throw new Exception("RuleValidator not supported");
        }

        private static void CheckOperator(Type ntype, SyntaxToken OperatorToken, List<Operator> operators)
        {
            var qinjectionop = Factory.GetOperator(OperatorToken);
            RuleValidator.CheckOperator(ntype, qinjectionop, operators);
        }

        private static void CheckOperator(Type ntype, Operator qinjectionop, List<Operator> operators)
        {
            var count = operators.Where(x => x == qinjectionop).Count();
            if (count == 0)
                throw new Exception(String.Format("Operator {0} not supported for node {1}", qinjectionop, ntype));
            if (count > 1)
                throw new Exception(String.Format("Malformed operator-rule for node {0} on operator {1}", ntype, qinjectionop));
        }

        private static void CheckName(Type ntype, string name, List<string> names)
        {
            var count = names.Where(x => x == name).Count();
            if (count == 0)
                throw new Exception(String.Format("Name {0} not supported for node {1}", name, ntype));
            if (count > 1)
                throw new Exception(String.Format("Malformed name-rule for node {0} with name {1}", ntype, name));
        }

        private static void CheckNameRegex(Type ntype, string name, List<Regex> namesregex)
        {
            foreach (var r in namesregex)
            {
                if (!r.IsMatch(name, 0))
                    throw new Exception(String.Format("Regulare Expression Error: Name {0} not supported for node {1}", name, ntype));
            }
        }

    }
}
